/*
  Logging subroutine
*/

#include <msp430x16x.h>

#include "../sd/sd.h"

/** Converts hex to ascii *
void hex2ascii(int number, char *hex) {
  char digit, place;

  for (place=3;place>=0;place--) {
    digit=(char)(number & 0xF);
    if (digit<10) {
      hex[place]=48+digit;
    } else {
      hex[place]=55+digit;
    }
    number/=16;
  }
}

* Write a log entry to the SD card *
int log_entry()
{
  int attitude[3], i, temperature;
  int j, current, time;
  unsigned char output[LOG_SIZE];
  char hex[4];
  unsigned long int address;

  //TODO: Get time index, increment, and add to output

  current=0;
  hex2ascii(time, hex); //convert time index to ascii
  for (j=0;j<4;j++) { //loop through 4 digits and store in output
    output[current++]=hex[j];
  }
  output[current++]=',';

  for(j=0;j<4;j++) output[current++]='0'; //No event occured
  output[current++]=',';
  for(j=0;j<4;j++) output[current++]='0'; //No event data
  output[current++]=',';

  //Hangs the MCU
  *adcEnable(1);
  temperature=adcSample(10,1);
  ADCS(attitude);
  adcDisable();*

  hex2ascii(temperature, hex);
  for (j=0;j<4;j++) { //loop through 4 digits and store in output
    output[current++]=hex[j];
  }
  output[current++]=',';

  for(i=0;i<3;i++) {
    hex2ascii(attitude[i], hex); //convert component of attitude data to hex
    for(j=0;j<4;j++) { //loop through 4 digits and store in output
      output[current++]=hex[j];
    }
    output[current++]=',';
  }

  output[current++]=';'; //terminate the line


  * Writes ADCS and temp data 5 times for testing
  for(i=0; i<40; i++) {
    output[i]=(char)(attitude[0] & 0xff);
    output[i++]=(char)((attitude[0] & 0xff00)/256);
    output[i++]=(char)(attitude[1] & 0xff);
    output[i++]=(char)((attitude[1] & 0xff00)/256);
    output[i++]=(char)(attitude[2] & 0xff);
    output[i++]=(char)((attitude[2] & 0xff00)/256);
    output[i++]=(char)(temperature & 0xff);
    output[i++]=(char)((temperature & 0xff00)/256);
    }*

  if (bufferPointer+LOG_SIZE>511) {
    address=0; //Temporary, for flight code must get address of SD card
    *sd_write_block(address, buffer);
    bufferPointer=0;
    sdWait();*
  }

  //Copies output block into buffer
  for (i=0;i<LOG_SIZE;i++)
    buffer[bufferPointer++]=output[i];

  return 16;
}

* Writes a minilog entry to RAM for non-SD mode *
int minilog_entry(unsigned char *minilog) {
  int current=0;
  minilog[current++]='0'; //No event

  //TODO: Add minilog cautious data gathering

  return 8;
}
*/

/*********************************************************************************************************************************
   Functions moved from sd.c
*********************************************************************************************************************************/

/* locations of where each part is stored on the SD card */

#define sdFlightVar  0
#define sdLogMeta    0x800
  #define sdLogTbl1 0x800
  #define sdLogTbl2 0x801 /* the second log table is in a different sector to that of the first so that they
                             they both can not be destroyed by one single write */
#define sdLog        0x1000
#define sdImgMeta    0x200000
#define sdImg        0x200800
#define sdTop        0x3B9ACA /* should ideally be 0x400000 but isnt due to 2gb sd card being 2E9 bytes */

/*********************************************
 * function: sd_hash_record
 * notes: will add a 2byte hash in the hash location of the record passed - defined in sd.h, is defined as static as
          it is only called from this file (log.c)
 ********************************************/
static void sd_hash_record(char* record)
{
  unsigned int i;
  unsigned int* h=(unsigned int*)(&record[SDHashLocation]);
  for(i=0;i<SDHashLocation;++i)
    (*h)=(((*h)<<5)+(*h))^record[i];
  return;
}

/* macro used in logPreData to make the code more readable */
#define sdLogSectorNotValid ((sdvarlog.l >= sdImgMeta) || (sdvarlog.l < sdLog))

/* for power cycling */
#ifdef SDPOWERCYCLE
#define SDDISABLEPOWER sdDisable(); P4SEL&=~0x20; P4DIR&=~0x20; P4OUT&=~0x20
#define SDENABLEPOWER sdInit() /* not really needed */
#define SDHASPOWER ((P4SEL & 0x20) && (P4DIR & 0x20) && P4OUT & 0x20))
#endif

/*********************************************
 * function: logPreData
 * notes: will prepare the sd card ready for a write, this involves finding out which sector the record is to be
          written to. It will load the correct sector off the sd card and into the buffer ready for it to be
          written back to the sd card with the added record.
 ********************************************/
int logPreData(void)
{
  unsigned int i; /* temporary variable for loops */
  /* check to see what sector we should be on, if top, mid and btm are high (0xff) then
     we reload in the data, if the current sector is greater than the last sector, or less than the first sector
     then we also reload the data
   */
  if(((sdvarlog.b[2]==0xff) && (sdvarlog.b[1]==0xff) && (sdvarlog.b[0]==0xff)) || (sdLogSectorNotValid))
  {
    /* we have the start or fault condition
       we will now re-read in the sd card location data
     */
    sd_set_blocklen(3); /* change the block length to 3 bytes */
    sd_read_block_complex(sdLogTbl1,0,3); /* read in the 3 bytes from the location of the first sd log table */
    sd_wait_notbusy();
    /* place the contents of the first sd log table into sdvarlog
       NB intentionally stored back to front so that it can be easily read in a hex viewer
     */
    sdvarlog.b[2]=sd_buffer.r[0];
    sdvarlog.b[1]=sd_buffer.r[1];
    sdvarlog.b[0]=sd_buffer.r[2];
    /* if the sector address is higher than the highest possible sector, or if it is lower than the lowest possible sector */
    if(sdLogSectorNotValid)
    {
      /* we read in the values from the second table */
      sd_read_block_complex(sdLogTbl2,0,3); /* read in first 3 bytes from the second log table */
      sd_wait_notbusy();
      /* put contents into sdvarlog */
      sdvarlog.b[2]=sd_buffer.r[0];
      sdvarlog.b[1]=sd_buffer.r[1];
      sdvarlog.b[0]=sd_buffer.r[2];
      /* again test for the boundary conditions */
      if(sdLogSectorNotValid)
      {
        /* only at this point do we cycle round to the first possible sector
           this could be a good time to raise a log message so we know this has happened */
        sdvarlog.l=sdLog;
        /* we will now clear the sector prior to writing out the two log tables, so if the mcu is reset whilst at
           this point, it will do do the same action again rather than think this sector has just been completed */
        sd_set_blocklen(512);
        for(i=0;i<512;++i) /* cycle from 0 to 511, and set each byte to zero in buffer --------------------could remove padding from this blanking */
        {                  /* records in sector cancelled to zero as just reseting record number could affect data in reset situation */
          sd_buffer.r[i]=0;
        }
        sd_write_block(sdvarlog.l);
        sd_wait_notbusy();
        /* now we write out the log tables */
        sd_buffer.r[0]=sdvarlog.b[2];
        sd_buffer.r[1]=sdvarlog.b[1];
        sd_buffer.r[2]=sdvarlog.b[0];
        sd_write_block(sdLogTbl1);
        sd_wait_notbusy();
        sd_write_block(sdLogTbl2);
        sd_wait_notbusy();
      }
      else
      {
        /* to get here, our first log table must be pointing too high or too low, but the second table is valid
           we will therefore just write out the second table to the first location, the second location will be
           in the sd_buffer, so we can just call a write */
        /* change to 512 for writing - sd card limitation */
        sd_set_blocklen(512);
        /* write out block stored in buffer from the second log table position to the first log table position */
        sd_write_block(sdLogTbl1);
        sd_wait_notbusy();
      }
    }
  }
  /* we now need to check that the sector we are about to read in has room to write to */
  /* so we read only the first byte in */
  sd_set_blocklen(1);
  sd_read_block_complex(sdvarlog.l,0,1);
  sd_wait_notbusy();
  /* we then compare this to the max record number */
  if(sd_buffer.s.record_number < SDMaxRecord)
  {
    /* we have a valid sector that can be read in so we read it in */
    sd_set_blocklen(512);
    sd_read_block(sdvarlog.l); /* valid to call sd_read_block as blocklen is 512 */
    /* we do not call sd_wait_notbusy as we can still read in stuff whilst the adcs etc. is collecting */
    return 0;
  }
  else
  {
    /* we need to use the next sector, and therefore increase sdvarlog */
    /* we will first blank out the sector, then write the log tables as this stops us from thinking we've just written this */
    /* increase the sector number */
    ++sdvarlog.l;
    /* now we check to see if this is a valid sector */
    if(sdLogSectorNotValid)
    {
      /* if it isnt, we need to cycle round to the start
         this could be a good time to raise a log message so we know this has happened */
      sdvarlog.l=sdLog;
    }

    /* once we get to this point, we need to write out a blank sector, then update the log tables */
    sd_set_blocklen(512);
    for(i=0;i<512;++i) /* cycle from 0 to 511, and set each byte to zero in buffer -----------------------------could remove padding from this blanking */
    {                  /* records in sector cancelled to zero as just reseting record number could affect data in reset situation */
      sd_buffer.r[i]=0;
    }
    sd_write_block(sdvarlog.l);
    sd_wait_notbusy();
    /* now we write out the log tables */
    sd_buffer.r[0]=sdvarlog.b[2];
    sd_buffer.r[1]=sdvarlog.b[1];
    sd_buffer.r[2]=sdvarlog.b[0];
    sd_write_block(sdLogTbl1);
    sd_wait_notbusy();
    sd_write_block(sdLogTbl2);
    sd_wait_notbusy();
    /* we then clear the sdvarlog data from the buffer, ready for logPostData */
    sd_buffer.r[0]=0;sd_buffer.r[1]=0;sd_buffer.r[2]=0;
  }
  return 0;
}

/*********************************************
 * function: logPostData
 * notes: will take the data set out in the record, add the hash and write it to the sd card
 ********************************************/
int logPostData(_sd_record* data)
{
  /* hash it */
  sd_hash_record((char*)data);
  /* add it to the current buffer */
  sd_buffer.s.sd_record[(unsigned char)sd_buffer.s.record_number]=*data;
  /* increase the record number */
  ++sd_buffer.s.record_number;
  /* write it */
  sd_write_block(sdvarlog.l);
  return 0;
}
